package io.wins.vertx.service.impl;

import java.util.ArrayList;
import java.util.List;

import io.vertx.core.CompositeFuture;
import io.vertx.core.Future;
import io.vertx.core.Vertx;
import io.vertx.core.eventbus.Message;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.wins.vertx.entity.Concert;
import io.wins.vertx.entity.Tag;
import io.wins.vertx.service.BaseService;
import io.wins.vertx.service.ConcertService;

public class ConcertServiceImpl extends BaseService implements ConcertService {

    public ConcertServiceImpl(final Vertx vertx) {
        this.vertx = vertx;
    }

    @Override
    public ConcertService listAllConcert(final Message tMessage) {
        final JsonObject sqlMes = new JsonObject()
            .put(SQL, "select * from ds_concert order by updated_on desc");
        this.querySql(sqlMes, this.resultQuery(tMessage, false, null, "Error"));
        return this;
    }

    @Override
    public ConcertService listConcertByPage(final int page, final int limit, final Message tMessage) {
        final JsonObject sqlMes = new JsonObject()
            .put(SQL, "select * from ds_concert order by updated_on desc limit ?, ?")
            .put(PARAMS, new JsonArray().add(this.calcPage(page, limit)).add(limit));
        this.querySql(sqlMes, this.resultQuery(tMessage, false, null, "Error"));
        return this;
    }

    @Override
    public ConcertService listTypeConcertByPage(final String typeId, final int page, final int limit, final Message tMessage) {
        final JsonObject sqlMes = new JsonObject()
            .put(SQL, "select * from ds_concert where type = ? order by updated_on desc limit ?, ?")
            .put(PARAMS, new JsonArray().add(typeId).add(this.calcPage(page, limit)).add(limit));
        this.querySql(sqlMes, this.resultQuery(tMessage, false, null, "Error"));
        return this;
    }

    @Override
    public ConcertService getConcertById(final String id, final Message tMessage) {
        final JsonObject sqlMes = new JsonObject()
            .put(SQL, "select * from ds_concert where id = ?")
            .put(PARAMS, new JsonArray().add(id));
        this.querySql(sqlMes, this.resultQuery(tMessage, false, null, "Error"));
        return this;
    }

    @Override
    public ConcertService insertConcert(final Concert concert, final Message tMessage) {
        final JsonObject sqlMes = new JsonObject()
            .put(SQL, "insert into ds_concert (id, title, poster, short_desc, content, seat_type, seat_map_id, type, status, score, begin_on, min_price, place, tags, created_on, updated_on) values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, UNIX_TIMESTAMP(), UNIX_TIMESTAMP())")
            .put(PARAMS, new JsonArray()
                .add(concert.getId())
                .add(concert.getTitle())
                .add(concert.getPoster())
                .add(concert.getShortDesc())
                .add(concert.getContent())
                .add(concert.getSeatType())
                .add(concert.getSeatMapId())
                .add(concert.getType())
                .add(concert.getStatus())
                .add(concert.getScore())
                .add(concert.getBeginOn())
                .add(concert.getMinPrice())
                .add(concert.getPlace())
                .add(concert.getTags()));
        this.insertSql(sqlMes, this.resultUpdate(concert.id, tMessage, "error happened"));
        this.insertConcertChildren(concert);
        return this;
    }

    @Override
    public ConcertService deleteConcert(final String id, final Message tMessage) {
        final JsonObject sqlMes = new JsonObject()
            .put(SQL, "delete from ds_concert where id = ?")
            .put(PARAMS, new JsonArray().add(id));
        this.deleteSql(sqlMes, this.resultUpdate(tMessage, "error happened"));
        return this;
    }

    @Override
    public ConcertService updateConcert(final Concert concert, final Message tMessage) {
        final List<Future> futures = new ArrayList<>();
        final Future delCT = Future.future();
        futures.add(delCT);
        final JsonObject delSql1 = new JsonObject()
            .put(SQL, "delete from ds_concert_time where parent_id = ?")
            .put(PARAMS, new JsonArray().add(concert.getId()));
        this.deleteSql(delSql1, res -> {
            if (res.failed()) {
                this.logger.error("error delete concertTime");
                delCT.fail(res.cause());
            } else {
                delCT.complete();
            }
        });

        final Future delFV = Future.future();
        futures.add(delFV);
        final JsonObject delSql2 = new JsonObject()
            .put(SQL, "delete from ds_face_values where parent_id = ?")
            .put(PARAMS, new JsonArray().add(concert.getId()));
        this.deleteSql(delSql2, res -> {
            if (res.failed()) {
                this.logger.error("error delete faceValues");
                delFV.fail(res.cause());
            } else {
                delFV.complete();
            }
        });
        CompositeFuture.all(futures).setHandler(r -> {
            if (r.succeeded()) {
                final JsonObject sqlMes = new JsonObject()
                    .put(SQL, "update ds_concert set title = ?, poster = ?, short_desc = ?, place = ?, content = ?, seat_type = ?, seat_map_id = ?, type = ?, status = ?, score = ?, begin_on = ?, min_price = ?, tags = ?, updated_on = UNIX_TIMESTAMP() where id = ?")
                    .put(PARAMS, new JsonArray()
                        .add(concert.getTitle())
                        .add(concert.getPoster())
                        .add(concert.getShortDesc())
                        .add(concert.getPlace())
                        .add(concert.getContent())
                        .add(concert.getSeatType())
                        .add(concert.getSeatMapId())
                        .add(concert.getType())
                        .add(concert.getStatus())
                        .add(concert.getScore())
                        .add(concert.getBeginOn())
                        .add(concert.getMinPrice())
                        .add(concert.getTags())
                        .add(concert.getId()));
                this.updateSql(sqlMes, this.resultUpdate(concert.id, tMessage, "error happened"));
                this.insertConcertChildren(concert);
            } else {
                this.logger.error("error happen on update concert: {}", r.cause().getMessage());
                tMessage.fail(503, "error happen on update concert");
            }
        });
        return this;
    }

    @Override
    public ConcertService publish(final String id, final String status, final Message<JsonObject> tMessage) {
        final JsonObject sqlMes = new JsonObject()
            .put(SQL, "update ds_concert set status = ? where id = ?")
            .put(PARAMS, new JsonArray().add(status).add(id));
        this.updateSql(sqlMes, this.resultUpdate(id, tMessage, "error"));
        return this;
    }

    @Override
    public ConcertService createTag(final Tag tag, final Message<JsonObject> tMessage) {
        final JsonObject sqlMes = new JsonObject()
            .put(SQL, "insert into ds_tag (code, name) values (?, ?)")
            .put(PARAMS, new JsonArray().add(tag.getCode()).add(tag.getName()));
        this.insertSql(sqlMes, this.resultUpdate(tMessage, "error"));
        return this;
    }

    @Override
    public ConcertService listTags(final Message<JsonObject> tMessage) {
        final JsonObject sqlMes = new JsonObject()
            .put(SQL, "select * from ds_tag order by name");
        this.querySql(sqlMes, this.resultQuery(tMessage, false, null, "Error"));
        return this;
    }

    @Override
    public ConcertService listTags(final JsonArray codes, final Message<JsonObject> tMessage) {
        final StringBuilder sb = new StringBuilder();
        sb.append("select * from ds_tag where code in(");
        if (codes.isEmpty()) {
            sb.append("''");
        } else {
            for (int i = 0; i < codes.size(); i++) {
                sb.append('?');
                if (i < codes.size() - 1) sb.append(',');
            }
        }
        sb.append(')');
        final JsonObject sqlMes = new JsonObject()
            .put(SQL, sb.toString())
            .put(PARAMS, codes);
        this.querySql(sqlMes, this.resultQuery(tMessage, false, null, "Error"));
        return this;
    }

    @Override
    public ConcertService getFaceValues(final String pid, final Message<JsonObject> tMessage) {
        final JsonObject sqlMes = new JsonObject()
            .put(SQL, "select * from ds_face_values where parent_id = ?")
            .put(PARAMS, new JsonArray().add(pid));
        this.querySql(sqlMes, this.resultQuery(tMessage, false, null, "Error"));
        return this;
    }

    @Override
    public ConcertService getConcertTime(final String pid, final Message<JsonObject> tMessage) {
        final JsonObject sqlMes = new JsonObject()
            .put(SQL, "select * from ds_concert_time where parent_id = ?")
            .put(PARAMS, new JsonArray().add(pid));
        this.querySql(sqlMes, this.resultQuery(tMessage, false, null, "Error"));
        return this;
    }

    private void insertConcertChildren(final Concert concert) {
        concert.getFaceValues().forEach(e -> {
            final JsonObject childSql2 = new JsonObject()
                .put(SQL, "insert into ds_face_values(id,parent_id,name,price,count) values(?,?,?,?,?)")
                .put(PARAMS, new JsonArray()
                    .add(e.getId()).add(concert.getId()).add(e.getName()).add(e.getPrice()).add(e.getCount()));
            this.insertSql(childSql2, this.resultNoRespon("error"));
        });
        concert.getConcertTime().forEach(e -> {
            final JsonObject childSql1 = new JsonObject()
                .put(SQL, "insert into ds_concert_time(id, parent_id, date, time) values(?,?,?,?)")
                .put(PARAMS, new JsonArray()
                    .add(e.getId()).add(concert.getId()).add(e.getDate()).add(e.getTime()));
            this.insertSql(childSql1, this.resultNoRespon("error"));
        });
    }


    public ConcertService getConcertById2(final String id, final Message tMessage) {
        Future.<JsonObject>future(f -> {
            final JsonObject sqlMes = new JsonObject()
                .put(SQL, "select * from ds_concert where id = ?")
                .put(PARAMS, new JsonArray().add(id));
            this.querySql(sqlMes, res -> {
                if (res.succeeded()) {
                    final JsonObject json = res.result().getJsonObject(0);
                    f.complete(json);
                } else {
                    f.fail(String.format("Can not find concert by id: [%s]", id));
                }
            });
        }).compose(json ->
            Future.<JsonObject>future(f -> {
                final JsonObject sqlMes = new JsonObject()
                    .put(SQL, "select * from ds_concert_time where parent_id = ?")
                    .put(PARAMS, new JsonArray().add(id));
                this.querySql(sqlMes, res -> {
                    if (res.succeeded()) {
                        final JsonArray concertTime = res.result();
                        json.put("concertTime", concertTime);
                        f.complete(json);
                    } else {
                        f.fail(String.format("Can not find concert time by pid: [%s]", id));
                    }
                });
            })
        ).compose(json ->
            Future.<JsonObject>future(f -> {
                final JsonObject sqlMes = new JsonObject()
                    .put(SQL, "select * from ds_face_values where parent_id = ?")
                    .put(PARAMS, new JsonArray().add(id));
                this.querySql(sqlMes, res -> {
                    if (res.succeeded()) {
                        final JsonArray faceValues = res.result();
                        json.put("faceValues", faceValues);
                        f.complete(json);
                    } else {
                        f.fail(String.format("Can not find face values by pid: [%s]", id));
                    }
                });
            })
        ).setHandler(asyncResult -> {
            if (asyncResult.succeeded()) {
                final Concert concert = new Concert(asyncResult.result());
                System.out.println(concert.toJson());
                tMessage.reply(new JsonObject().put("request", tMessage.body()).put("result", concert.toJson()));
//                if (cache) {
//                    final JsonObject obj = result.getJsonObject(0);
//                    CacheManager.getInstance().set(obj.getString("id"), "concert", obj);
//                }
            } else {
                final String failedMessage = String.format("");
                this.logger.error(failedMessage, asyncResult.cause());
                tMessage.fail(500, failedMessage);
            }
        });
        return this;
    }
}
